package com.ihr360.expr.core;

import com.ihr360.expr.ExprArray;
import com.ihr360.expr.ExprDouble;
import com.ihr360.expr.ExprException;
import com.ihr360.expr.ExprInteger;
import com.ihr360.expr.ExprMissing;
import com.ihr360.expr.ExprString;
import com.ihr360.expr.context.IEvaluationContext;

public abstract class AbstractFunction implements IExprFunction {

    @Override
    public boolean isVolatile() {
        return false;
    }


    protected void assertArgCount(Expr[] args, int count) throws ExprException {
        if (args == null && count != 0) {
            throw new ExprException(getClass().getSimpleName() +
                    " function takes no arguments");
        }

        if (args.length != count){
            throw new ExprException(getClass().getSimpleName() +
                    " function takes " + count + " arguments");
        }
    }

    protected void assertArgCount(Expr[] args, int min, int max)
            throws ExprException {
        assertMinArgCount(args, min);
        assertMaxArgCount(args, max);
    }

    protected void assertMinArgCount(Expr[] args, int count)
            throws ExprException {
        if (args.length < count){
            throw new ExprException("Too few arguments to function " +
                    getClass().getSimpleName());
        }
    }

    protected void assertMaxArgCount(Expr[] args, int count)
            throws ExprException {
        if (args.length > count){
            throw new ExprException("Too many arguments to function " +
                    getClass().getSimpleName());
        }
    }

    protected double asDouble(IEvaluationContext context, Expr arg,
                              boolean strict) throws ExprException {
        if (arg instanceof ExprEvaluatable) {
            arg = ((ExprEvaluatable) arg).evaluate(context);
        }
        if (arg instanceof ExprNumber) {
            return ((ExprNumber) arg).doubleValue();
        }
//        if (arg instanceof ExprMissing){
//            return 0;
//        }
        if (!strict){
            return 0;
        }
        throw new ExprException("Invalid argument type for function " +
                getClass().getSimpleName());
    }

    protected int asInteger(IEvaluationContext context, Expr arg, boolean strict)
            throws ExprException {
        if (arg instanceof ExprEvaluatable) {
            arg = ((ExprEvaluatable) arg).evaluate(context);
        }
        if (arg instanceof ExprNumber) {
            return ((ExprNumber) arg).intValue();
        }
        if (!strict){
            return 0;
        }
        throw new ExprException("Invalid argument type for function " +
                getClass().getSimpleName());
    }

    protected boolean asBoolean(IEvaluationContext context, Expr arg,
                                boolean strict) throws ExprException {
        if (arg instanceof ExprEvaluatable) {
            arg = ((ExprEvaluatable) arg).evaluate(context);
        }
        if (arg instanceof ExprNumber) {
            return ((ExprNumber) arg).booleanValue();
        }
        if (!strict){
            return false;
        }
        throw new ExprException("Invalid argument type for function " +
                getClass().getSimpleName());
    }

    protected String asString(IEvaluationContext context, Expr arg,
                              boolean strict) throws ExprException {
        if (arg instanceof ExprEvaluatable) {
            arg = ((ExprEvaluatable) arg).evaluate(context);
        }
        if (arg instanceof ExprString) {
            return ((ExprString) arg).str;
        }
        if (!strict) {
            if (arg instanceof ExprNumber) {
                return arg.toString();
            }
            return "";
        }
        throw new ExprException("Invalid argument type for function " +
                getClass().getSimpleName());
    }


    protected int getLength(Expr range) {
        if (range instanceof ExprArray) {
            return ((ExprArray) range).length();
        } else {
            return 1;
        }
    }

    protected Expr get(Expr range, int index) {
        if (range instanceof ExprArray) {
            ExprArray a = (ExprArray) range;
            if (index >= 0 && index < a.length()) {
                return a.get(index);
            }
        } else if (index == 0) {
            return range;
        }

        return ExprMissing.MISSING;
    }

    protected boolean isNumber(Expr x) {
        return x instanceof ExprDouble || x instanceof ExprInteger;
    }
}